package com.eflow.engine.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.eflow.engine.service.IEflowTaskService;
import com.eflow.engine.service.ITaskService;
import com.eflow.engine.vo.CompleteTaskVo;
import com.eflow.engine.vo.PassPathVo;
import com.eflow.engine.vo.ProcessVo;
import com.eflow.model.domain.EflowTask;
import com.eflow.model.expetion.FlowableException;
import com.eflow.model.e.BpmConst;
import com.eflow.engine.service.IProcessService;
import com.eflow.engine.vo.StartProcessInstanceVo;
import com.eflow.model.utils.FlowUtil;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.common.engine.impl.util.IoUtil;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author hui se
 * @description
 * @create 2021-02-25 11:25
 **/
@Transactional(rollbackFor = Exception.class)
public class ProcessServiceImpl implements IProcessService, BpmConst {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    protected IdentityService identityService;
    @Autowired
    protected TaskService taskService;
    @Autowired
    protected ITaskService eTaskService;
    @Autowired
    protected HistoryService historyService;
    @Autowired
    private IEflowTaskService eflowTaskService;
    @Override
    public ProcessInstance startProcessInstanceByKey(StartProcessInstanceVo params) {
        if (StringUtils.isBlank(params.getProcessDefinitionKey())
                || StringUtils.isBlank(params.getBusinessKey()) || StringUtils.isBlank(params.getTitle())) {
            throw new FlowableException("请填写 这三个字段 processDefinitionKey,businessKey,title");
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(params.getProcessDefinitionKey())
                .latestVersion().singleResult();
        return startProcessInstance(params, processDefinition);
    }

    private ProcessInstance startProcessInstance(StartProcessInstanceVo params, ProcessDefinition processDefinition) {
        if (processDefinition != null && processDefinition.isSuspended()) {
            throw new FlowableException("此流程已经挂起,请联系系统管理员!");
        }
        params.getVariables().put(VAR_PROCESS_TITLE, params.getTitle());
        if (CollectionUtil.isNotEmpty(params.getUserList())) {
            params.getVariables().putAll(params.getUserListMap());
        }
        ProcessInstance processInstance = runtimeService.createProcessInstanceBuilder()
                .processDefinitionKey(params.getProcessDefinitionKey())
                .processDefinitionId(params.getProcessDefinitionId())
                .name(params.getTitle())
                .transientVariable(SKIP_LIST,params.getSkipList())
                .businessKey(params.getBusinessKey().trim())
                .variables(params.getVariables())
                .start();

        //自动跳过第一步
        List<Task> list = taskService.createTaskQuery().processInstanceId(processInstance.getId()).active().list();
        if (list.isEmpty()) {
            throw new FlowableException("没有下一步任务节点");
        }
        if (list.size() > 1) {
            throw new FlowableException("开始任务节点只能存在一个");
        }
        Task task = list.get(0);
        CompleteTaskVo completeTaskVo = new CompleteTaskVo();
        completeTaskVo.setTaskId(task.getId());
        completeTaskVo.setMessage(params.getMessage());
        completeTaskVo.setProcessInstanceId(processInstance.getProcessInstanceId());
        completeTaskVo.setVariables(params.getVariables());
        completeTaskVo.setUserList(params.getUserList());
        completeTaskVo.setSkipList(params.getSkipList());
        if(StrUtil.isBlank(completeTaskVo.getMessage())){
            completeTaskVo.setMessage("[提交]");
        }
        eTaskService.complete(completeTaskVo);
        return processInstance;
    }

    @Override
    public ProcessInstance startProcessInstanceById(StartProcessInstanceVo params) {
        if (StringUtils.isBlank(params.getProcessDefinitionId())
                || StringUtils.isBlank(params.getBusinessKey()) || StringUtils.isBlank(params.getTitle())) {
            throw new FlowableException("请填写 这三个字段 ProcessDefinitionId,businessKey,title");
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(params.getProcessDefinitionId())
                .singleResult();
        return startProcessInstance(params, processDefinition);
    }


    @Override
    public PassPathVo getPassPath(String processInstanceId) {
        PassPathVo passPathVo = new PassPathVo();
        //1.获取当前的流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        String processDefinitionId = null;
        List<String> activeActivityIds = null;
        List<String> highLightedFlows = new ArrayList<>();
        //2.获取所有的历史轨迹对象
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).list();
        Map<String, HistoricActivityInstance> hisActivityMap = new HashMap<>();
        list.forEach(historicActivityInstance -> {
            if (!hisActivityMap.containsKey(historicActivityInstance.getActivityId())) {
                hisActivityMap.put(historicActivityInstance.getActivityId(), historicActivityInstance);
            }
        });
        //3. 获取流程定义id和高亮的节点id
        if (processInstance != null) {
            //3.1. 正在运行的流程实例
            processDefinitionId = processInstance.getProcessDefinitionId();
            activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
            passPathVo.setCurrentPath(activeActivityIds);
        } else {
            //3.2. 已经结束的流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId = historicProcessInstance.getProcessDefinitionId();
            activeActivityIds = new ArrayList<>();
            List<EndEvent> endEvents = FlowUtil.findEndFlowElement(repositoryService, processDefinitionId);
            for (EndEvent endEvent : endEvents) {
                if (hisActivityMap.containsKey(endEvent.getId())) {
                    activeActivityIds.add(endEvent.getId());
                    highLightedFlows.add(endEvent.getId());
                }
            }

        }
        //4. 获取流程定义的所有节点信息
        Collection<FlowElement> flowNodes = FlowUtil.getAllFlowElements(repositoryService,processDefinitionId);
        Map<String, FlowElement> activityMap = flowNodes.stream().collect(Collectors.toMap(FlowElement::getId, flowNode -> flowNode));
        //5. 递归得到高亮线
        activeActivityIds.forEach(activeActivityId -> this.getHighLightedFlows(activityMap, hisActivityMap, activeActivityId, highLightedFlows, activeActivityId));
        passPathVo.setPassPath(highLightedFlows);
        return passPathVo;

    }


    @Override
    public ProcessVo getProcessXmlByProcessInstanceId(String processInstanceId){

        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if(processInstance==null){
            throw  new FlowableException("未找到该流程实例");
        }
        return  getProcessXmlByProcessDefinitionId(processInstance.getProcessDefinitionId());
    }

    @Override
    public ProcessVo getProcessXmlByProcessDefinitionId(String ProcessDefinitionId) {
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(ProcessDefinitionId);
        if(processDefinition==null){
            throw  new FlowableException("未找到该流程定义");
        }
        ProcessVo processVo = new ProcessVo();
        InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
        byte[] bytes = IoUtil.readInputStream(inputStream, processDefinition.getResourceName());
        processVo.setProcessName(processDefinition.getName());
        processVo.setProcessDefinitionId(processDefinition.getId());
        processVo.setXml(new String(bytes));
        return  processVo;
    }

    private void getHighLightedFlows(Map<String, FlowElement> flowNodeMap,
                                     Map<String, HistoricActivityInstance> hisActivityMap,
                                     String activeActivityId,
                                     List<String> highLightedFlows,
                                     String oldActivityId) {
        FlowNode flowNode = (FlowNode)flowNodeMap.get(activeActivityId);
        List<SequenceFlow> incomingFlows = flowNode.getIncomingFlows();
        for (SequenceFlow sequenceFlow : incomingFlows) {
            String sourceRefId = sequenceFlow.getSourceRef();
            if (hisActivityMap.containsKey(sourceRefId) && !oldActivityId.equals(sourceRefId)) {
                highLightedFlows.add(sequenceFlow.getId());
                highLightedFlows.add(sourceRefId);
                this.getHighLightedFlows(flowNodeMap, hisActivityMap, sourceRefId, highLightedFlows, oldActivityId);
            } else {
                if (hisActivityMap.containsKey(sourceRefId)) {
                    highLightedFlows.add(sequenceFlow.getId());
                    highLightedFlows.add(sourceRefId);
                }
                break;
            }
        }
    }

    @Override
    public void deleteProcessInstance(String processInstanceId, String deleteReason){
        long count = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).count();
        if(count>0){
            runtimeService.deleteProcessInstance(processInstanceId,deleteReason);
            eflowTaskService.remove(Wrappers.lambdaQuery(EflowTask.class).eq(EflowTask::getProcessInstanceId,processInstanceId));
        }
    }

}
